
"""
交易执行器：处理订单提交和账户管理
"""
import os
import random
import pandas as pd
from datetime import datetime
from xtquant import xtconstant
from xtquant.xttrader import XtQuantTrader, XtQuantTraderCallback
from xtquant.xttype import StockAccount, XtOrder
from typing import Dict, List
from .models import Position, Order, OrderStatus, OrderDirection, OrderType
from .exceptions import TradeError, RetryableError
from config.config import config
from utils.logger import sys_logger

logger = sys_logger.getChild('Trader')

# 设置pandas打印选项，强制显示所有行和列
pd.set_option('display.max_rows', None)
pd.set_option('display.max_columns', None)
pd.set_option('display.width', 10000)
pd.set_option('display.max_colwidth', None)  # 显示完整列内容
pd.set_option('display.unicode.east_asian_width', True)

# 建立xtconstant到OrderStatus的映射字典
OREDER_STATUS_MAPPING = {
    # xtconstant常量值 : OrderStatus成员
    xtconstant.ORDER_UNREPORTED     : OrderStatus.PENDING,         # 48 未报
    xtconstant.ORDER_WAIT_REPORTING : OrderStatus.PENDING,         # 49 待报
    xtconstant.ORDER_REPORTED       : OrderStatus.SUBMITTED,       # 50 已报
    xtconstant.ORDER_REPORTED_CANCEL: OrderStatus.CANCELLED,       # 51 已报待撤
    xtconstant.ORDER_PARTSUCC_CANCEL: OrderStatus.CANCELLED,       # 52 部成待撤
    xtconstant.ORDER_PART_CANCEL    : OrderStatus.CANCELLED,       # 53 部撤
    xtconstant.ORDER_CANCELED       : OrderStatus.CANCELLED,       # 54 已撤
    xtconstant.ORDER_PART_SUCC      : OrderStatus.PARTIAL_FILLED,  # 55 部成
    xtconstant.ORDER_SUCCEEDED      : OrderStatus.FILLED,          # 56 已成
    xtconstant.ORDER_JUNK           : OrderStatus.REJECTED,        # 57 废单
    xtconstant.ORDER_UNKNOWN        : OrderStatus.UNKNOWN          # 255 未知
}

OREDER_TYPE_MAPPING = {
    # xtconstant常量值 : OrderDirection成员
    xtconstant.STOCK_BUY  : OrderDirection.BUY,
    xtconstant.STOCK_SELL : OrderDirection.SELL
}

def convert_order_status(order_status: int) -> OrderStatus:
    """
    将xtconstant订单状态转换为统一枚举状态
    :param order_status: 来自order.order_status的原始值
    :return: OrderStatus枚举成员
    """
    return OREDER_STATUS_MAPPING.get(order_status, OrderStatus.UNKNOWN)

def convert_order_type(order_type: int) -> OrderStatus:
    """
    将xtconstant订单状态转换为统一枚举状态
    :param order_type: 来自order.order_type的原始值
    :return: OrderStatus枚举成员
    """
    return OREDER_TYPE_MAPPING.get(order_type)

class MyXtQuantTraderCallback(XtQuantTraderCallback):
    # 更多说明见 http://dict.thinktrader.net/nativeApi/xttrader.html?id=I3DJ97#%E5%A7%94%E6%89%98xtorder
    def __init__(self, engine):
        self._engine = engine

    def on_connected(self):
        """
        连接成功推送
        """
        print(datetime.now(), "callback, on_connected")

    def on_disconnected(self):
        """
        连接断开推送
        """
        print(datetime.now(), "callback, on_disconnected")
        self._engine.trader.status = False

    def on_stock_order(self, order: XtOrder):
        """
        委托回报推送
        :param order: XtOrder对象
        :return:
        """
        try:
            # 转换订单状态
            _id = order.order_remark
            symbol = order.stock_code
            order_id = order.order_id
            price = order.price
            volume = order.order_volume
            traded_volume = order.traded_volume
            traded_price = order.traded_price
            price_type = order.price_type

            status = convert_order_status(order.order_status)
            direction = convert_order_type(order.order_type)

            logger.debug("委托回报推送 | 订单号 %d | %s %s %s | 下单 %d股 @ %.3f | 成交 %d股 @ %.3f | 订单类型 %d",
                  order_id, status.value, symbol, direction.value, volume, price, traded_volume, traded_price, price_type)

            #生成订单类
            _order = Order(symbol = symbol,
                           direction = direction,
                           price = price,
                           volume = volume
                           )

            _order.set_id(_id)
            _order.set_order_id(order_id)
            _order.update_status(status, traded_volume, traded_price)

            self._engine.process_trader_order_callback(_order)
        except Exception:
            logger.error("委托回报推送异常", exc_info=True)
            raise ValueError("委托回报推送异常")

    def on_order_error(self, order_error):
        """
        :param order_error: XtOrderError 对象
        :return:
        """
        try:
            _id = order_error.order_remark
            order_id = order_error.order_id

            status = OrderStatus.REJECTED

            logger.error("委托错误回报推送 | %d %s",
                  order_id, status.value)

            #生成订单类
            _order = Order(symbol = "000000.SH",
                           direction = OrderDirection.BUY,
                           price = 0,
                           volume = 0
                           )

            _order.set_id(_id)
            _order.set_order_id(order_id)
            _order.update_status(status, 0, 0)

            self._engine.process_trader_order_callback(_order)
        except Exception:
            logger.error("委托错误回报推送异常", exc_info=True)
            raise ValueError("委托错误回报推送异常")

    def on_cancel_error(self, cancel_error):
        """
        :param cancel_error:XtCancelError 对象
        :return:
        """
        logger.error("撤销委托错误回报推送, 暂未执行任何处理")

class TradeExecutor:
    def __init__(self):
        """
        初始化交易执行器
        :param account_id: 迅投账户ID
        """
        self._config = config
        self._account_id = self._config.get('account.account_id')
        self._positions: Dict[str, Position] = {}  # 各标的持仓

        self.status = False

    def _validate_config(self) -> None:
        """验证配置文件中的必要参数"""
        self.config = config
        self.plugin_path = self.config.get("xt.plugin_path")
        #self.session_id = self.config.get("xt.session_id")
        self.session_id = int(random.randint(100000, 999999))  # 生成一个随机的会话ID

        if not os.path.exists(self.plugin_path):
            raise FileNotFoundError(f"XT插件路径不存在: {self.plugin_path}")
        if not self.session_id:
            raise ValueError("配置文件中缺少 session_id")

    def bind(self, engine):
        self._engine = engine

    def _register_callback(self):
        # 注册回调函数
        self._callback_handler = MyXtQuantTraderCallback(self._engine)
        self._trader.register_callback(self._callback_handler)

    def _initialize(self) -> None:
        """初始化交易接口"""
        try:
            self._trader = XtQuantTrader(
                path=self.plugin_path,
                session=self.session_id
            )
            logger.info(f"交易接口初始化成功 session_id: {self.session_id}")
        except Exception as e:
            logger.error(f"交易接口初始化失败: {str(e)}")
            raise TradeError("交易接口初始化失败") from e

    def _connect(self) -> bool:
        """连接交易账户"""
        try:
            self._trader.start()
            res = self._trader.connect()
            if res == 0:
                logger.info("交易客户端连接成功")
                return True
            else:
                self._trader.stop()
                logger.error("交易客户端连接失败")
                return False
        except Exception:
            logger.error("交易客户端连接失败")

    def _login(self) -> None:
        """账号认证"""
        try:
            self._account = StockAccount(self._account_id)
            res = self._trader.subscribe(self._account)
            if res == 0:
                logger.info(f"交易账户订阅成功, 账号: {self._account_id}")
            else:
                logger.error(f"交易账户订阅失败, 账号: {self._account_id}")
                raise RetryableError("交易账户订阅失败, 账号: {self._account_id}")
        except Exception as e:
            logger.error(f"登录失败, 账号: {self._account_id}: {str(e)}")
            raise RetryableError("登录失败, 账号: {self._account_id}") from e

    def disconnect(self) -> None:
        res = self._trader.unsubscribe(self._account)
        if res == 0:
            logger.info(f"交易账户取消订阅成功, {self._account_id}")
        else:
            logger.error(f"交易账户取消订阅失败, {self._account_id}")

        self._trader.stop()

    def start(self) -> bool:
        self._validate_config()
        self._initialize()
        self._register_callback()
        if not self._connect():
            self.status = False
            return False
        self._login()

        self.status = True
        return True

    def place_order(self, order: Order) -> int:
        """
        提交订单
        :param order: Order对象
        :return: 券商订单ID
        """
        try:
            # 转换订单方向
            direction = xtconstant.STOCK_BUY if order.direction == OrderDirection.BUY else xtconstant.STOCK_SELL
            type = xtconstant.MARKET_PEER_PRICE_FIRST if order.type == OrderType.MARKET else xtconstant.FIX_PRICE

            # 提交订单
            xt_order_id = self._trader.order_stock(
                account=self._account,
                stock_code=order.symbol,
                order_type=direction,
                order_volume=order.volume,
                price_type=type,
                price=order.price,
                order_remark=order.id
            )

            if xt_order_id > 0:
                order.set_order_id(xt_order_id)
                logger.info("订单提交成功 | 订单号 %d | %s %s %d股 @ %.3f | 订单类型 %s",
                      xt_order_id, order.symbol, order.direction.value, order.volume, order.price, order.type.value)
            else:
                logger.info("订单提交失败 | 订单号 %d | %s %s %d股 @ %.3f | 订单类型 %s",
                      xt_order_id, order.symbol, order.direction.value, order.volume, order.price, order.type.value)

            return xt_order_id

        except Exception as e:
            logger.error("TradeExecutor place_order 执行失败", exc_info=True)
            raise TradeError("TradeExecutor place_order 执行失败") from e

    def cancel_order(self, order: Order) -> bool:
        """撤单操作"""
        res = self._trader.cancel_order_stock(self._account, order.order_id)
        if res == 0:
            logger.info("撤单操作成功 | 订单号 %d | %s %s %d股 @ %.3f",
                  order.order_id, order.symbol, order.direction.value, order.volume, order.price)
            return True
        else:
            logger.info("撤单操作失败 | 订单号 %d | %s %s %d股 @ %.3f",
                  order.order_id, order.symbol, order.direction.value, order.volume, order.price)
            return False

    def query_orders(self) -> None:
        """获取当日全部委托"""
        # 查询账户委托
        orders = self._trader.query_stock_orders(self._account)

        # 处理查询结果，将其转换为 DataFrame 格式
        list_orders = []
        for i in orders:
            orders_dict = {
                '资金账号': i.account_id,
                '证券代码': i.stock_code,
                '订单编号': i.order_id,
                '柜台合同编号': i.order_sysid,
                '报单时间': i.order_time,
                '委托类型': i.order_type,
                '委托数量': i.order_volume,
                '报价类型': i.price_type,
                '委托价格': i.price,
                '成交数量': i.traded_volume,
                '成交均价': i.traded_price,
                '委托状态': i.order_status,
                '委托状态描述': i.status_msg,
                '策略名称': i.strategy_name,
                '委托备注': i.order_remark,
            }
            list_orders.append(orders_dict)

        # 将列表转换为 DataFrame
        df_orders = pd.DataFrame(list_orders)

        logger.info(f"当前服务器显示的委托订单\n{df_orders}")

    def query_trades(self) -> List[Dict]:
        """获取当日成交明细"""
        return []

    def query_positions(self) -> Dict:
        """获取当前持仓"""
        try:
            self._positions.clear()

            positions = self._trader.query_stock_positions(self._account)

            list_positions = []

            for i in positions:
                self._positions[i.stock_code] = Position(symbol = i.stock_code)
                self._positions[i.stock_code].update_positon(i.volume, i.can_use_volume, i.avg_price)

                dict_position = {
                    '证券代码': i.stock_code,
                    '持仓数量': i.volume,
                    '可用数量': i.can_use_volume,
                    '市值'    : i.market_value,
                    '成本价'  : i.avg_price,
                }

                list_positions.append(dict_position)

            df_positions = pd.DataFrame(list_positions)  # 将持仓信息转换为 DataFrame 格式

            logger.debug(f"当前服务器显示的持仓\n{df_positions}")

            return self._positions
        except Exception:
            logger.error("TradeExecutor query_positions 执行失败", exc_info=True)
            raise TradeError("TradeExecutor query_positions 执行失败")


    def display_positions(self) -> None:
        list_positions = []

        for symbol in self._positions:
            position = self._positions[symbol]
            dict_position = {
                '证券代码': position.symbol,
                '持仓数量': position.total_volume,
                '可用数量': position.available_volume,
                '成本价'  : position.avg_price,
            }

            list_positions.append(dict_position)

        df_positions = pd.DataFrame(list_positions)  # 将持仓信息转换为 DataFrame 格式

        logger.info(f"当前本地缓存显示的持仓\n{df_positions}")

    def query_asset(self) -> float:
        """获取当前资金信息"""
        try:
            asset = self._trader.query_stock_asset(self._account)
            list_asset = []
            dict_asset = {
                '总资产':   asset.total_asset,
                '可用资金': asset.cash,
                '冻结资金': asset.frozen_cash,
                '持仓市值': asset.market_value
            }
            list_asset.append(dict_asset)
            df_asset = pd.DataFrame(list_asset)  # 将资金信息转换为 DataFrame 格式
            logger.debug(f"当前服务器显示的资金信息\n{df_asset}")

            return asset.cash
        except Exception:
            logger.error("TradeExecutor query_asset 执行失败", exc_info=True)
            raise TradeError("TradeExecutor query_asset 执行失败")